package io.eyer.nio.core.event;

import io.eyer.nio.core.session.Session;
import io.eyer.nio.core.support.NioConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;

/**
 * Created by 青木 on 2015/8/17.
 * 读写事件反应堆
 */
public class Reactor extends NioAbstract<Object[]> {
    private static Logger logger = LoggerFactory.getLogger(Reactor.class);

    public Reactor() throws IOException {
    }

    public void registeredChannel(SocketChannel socketChannel, int ops) throws ClosedChannelException {
        if (this == currentThread()) {
            SelectionKey key = socketChannel.register(this.selector, ops);
            Session session = new Session(socketChannel, key, new Date());
            key.attach(session);
            NioConfig.getHandler().onSessionCreated(session);
        } else {
            registeredQueue.offer(new Object[]{socketChannel, ops, null});
            this.selector.wakeup();
        }
    }

    public void registeredSession(SocketChannel socketChannel, int ops, Session session) throws ClosedChannelException {
        if (this == currentThread()) {
            SelectionKey key = socketChannel.register(this.selector, ops, session);
            session.setKey(key);
        } else {
            Reactor.this.registeredQueue.offer(new Object[]{socketChannel, ops, session});
            Reactor.this.selector.wakeup();
        }
    }


    @Override
    public Logger getLogger() {
        return logger;
    }


    @Override
    protected void handleRegistered(Object[] item, Selector selector) throws ClosedChannelException {
        SocketChannel socketChannel = (SocketChannel) item[0];
        Session session = (Session) item[2];
        if (session != null) {
            SelectionKey key = socketChannel.register(selector, (int) item[1], session);
            session.setKey(key);
            session.getLatch().countDown();
        } else {
            SelectionKey key = socketChannel.register(selector, (int) item[1]);
            session = new Session(socketChannel, key, new Date());
            key.attach(session);
        }
        NioConfig.getHandler().onSessionCreated(session);
    }

    @Override
    public void handleKey(SelectionKey key) {
        try {
            if (key.isValid()) {
                Session session = (Session) key.attachment();
                session.updateLastTime();
                if (key.isReadable()) {
                    NioConfig.getSessionHandler().handleRead(session);
                } else if (key.isWritable()) {
                    NioConfig.getSessionHandler().handleWrite(session);
                } else {
                    key.cancel();
                }
            }
        } catch (Exception e) {
            Session session = (Session) key.attachment();
            if (session != null) {
                NioConfig.getHandler().onException(session, e);
            }
            try {
                key.channel().close();
                key.cancel();
            } catch (IOException e1) {
                logger.error(e.getMessage(), e);
            }
            logger.error(session.toString(), e);

        }
    }


}
